引介 | Beam Sync:同步以太坊节点的新方法
为什么要改善节点同步体验?
每当我想起目前还有多少人仍在使用 Infura(通过 Metamask、Gnosis-Safe 等)与链上应用程序交互时,我就感到有点难受。Infura 的服务很棒,但如果大部分用户都不自己运行节点,显然也不太对头。即使是非常有能力且积极性非常高的开发人员,也不能完全摆脱对 Infura 的依赖。从这点看来,我们仍未完成以太坊的 “自主验证” 愿景中的重要部分。
我们的团队希望尽自己的力量扭转这一趋势。我们的使命是尽可能增加网络上的节点数量,特别是那些由爱好者、研究人员和开发人员运行的节点。当我们问起他们为何不运行自己节点时,答案不外乎是:“我安装了客户端软件,也尝试了同步区块链,但好像总是同步不上。于是我就停下来了,因为我还有别的事要做。”
所以,如果我们想让更多的人来运行节点,就得让节点同步更快一些,并且在同步过程中能够反馈进度信息。许多团队都在这个领域耕耘。专用硬件或许是一条重要途径。但在这篇文章里,我们想讲的是 “Beam Sync” 同步方法如何能够大幅提升同步速度。
现有的同步方法
为了更好地理解 Beam Sync 的原理,我们先来看看现有的几种同步方法。
Full Sync(完全同步方法)
完全同步方法是执行自创世区块以来的每一个区块。创世区块标志着一个起始创世状态(状态的内容包括帐户余额、合约字节码、合约存储内容等)。所谓 “执行区块” 就是,每下载到一个区块,就读取前一个状态并(根据区块内容)产生新状态,并以该新状态来验证区块头中的状态根(以验证该区块是不是一个有效的区块)。在以太坊主网上完全同步的速度非常慢,而且随着网络的老化,使用完全同步方法来同步到最新区块的时间也会越来越长。所以人们开发出了 “快速同步” 方法。
Fast Sync(快速同步方法)
快速同步方法就是下载过去所有的区块和区块头,并选择最近的区块作为 “启动块”。启动块以前的区块都跳过执行,到了启动块再开始执行区块。这种方法假设了从创世块到启动块都正确地遵循了所有 EVM 规则。这个假设是合理的,因为矿工有动力遵循诚信不作恶原则,生产正常区块,拒绝可能具有攻击性的区块。
在快速同步可以执行启动块之前,需要的区块状态包括:合约字节码、帐户和合约存储内容。执行交易时可能需要读取所有这些值中的任何一个。因此,快速同步方法要求从其他对等节点处获得启动块之前的状态快照。快照是用状态根哈希值来标记的;所谓状态根哈希值,就是所有状态内容的哈希默克尔树根值。节点使用该状态根哈希来验证从其他对等节点处下载的状态数据是否与矿工在该区块中声明的状态相匹配。
快速同步方法下载完所需的所有状态后,等于节点已经有了执行交易所需的一切数据。那么这时候开始,节点就可以切换成完全同步模式了,从启动块开始可以逐个逐个执行区块了,就跟完成了启动块以前的完全同步过程的节点一样。
简化之后的过程就像下面这张动图所显示的:
其他方法
其他的快速同步方法包括 Warp Sync 以及一些目前尚未得到验证的同步方法。抽象一些来看,它们都属于快速同步方法的不同形式。另外,即使了解这些其它同步方法的原理,也无助于理解 Beam 同步策略,所以这些同步原理不是我们这篇文章的重点,我以后再讲。
快速同步方法有多快?
快速同步方法在目前的主网运行环境下面临一些挑战,因为同步需要下载很多数据,甚至超过 100GB 的数据,所以,可能在上图所示的第二步 “Get All State(获得所有的状态)” 就要卡住很长时间。
更糟糕的是,(在快速同步模式下)对等节点不会逐块给你提供状态数据,只会提供启动块前一段时间内的状态,比如启动块前 100 个区块(即开始同步前 30 分钟)的状态数据。Geth 客户端的默认设置是前 120 个区块。
如果你不能在 30 分钟内下载完所有的状态数据(剧透警告:你真的下不完),你就需要做切换(pivot),就是换一个新的启动块,重新开始同步,虽然不是从 0 开始,但是也增加了下载和验证区块的时间。
Geth 客户端在提高同步速度上取得了非凡的成绩,快速同步和完全同步模式都有巨大进步,而且 Geth 客户端的每一次更新都会有所推进,但即使你有非常完美的电脑硬件,同步过程依然需要持续至少 4 小时。对于第一次同步来说,这样的过程确实略显艰难。
那么,我们团队正在开发一个用 Python 语言编写的客户端,叫 “Trinity”。Python 在速度性能上不会比 Go 语言更快。如果以性能为中心的 Geth 代码不能像我们希望的那样快速同步,Trinity 客户端又有什么机会呢?完全有理由预计 Trinity 客户端执行一次快速同步需要几个星期。但是如果客户端不能同步主网,那么它就没意义,同样,花费几周时间去同步也没有意义。出于这种需要,我们构思了一种新的同步策略,我们现在称之为:Beam 同步方法。
Beam 同步方法
概述
Beam 同步方法是直接改进快速同步方法的结果,这两种同步方法的区别是 Beam 同步方法是一开始就直接执行启动块,并且只请求本地数据库中缺少的状态数据,并把输入状态和输出状态保存在本地。执行完一个块后就同步到下一个块并重复该过程,按需请求缺少的数据。
随着时间的推移,缺少的数据会越来越少。注意,如果某个状态从未被访问过,那么客户端将永远不会请求它(因此永远也不会获得这部分状态数据),因此我们在后台运行另一个进程来填补这些空白。通过该回填过程,Beam 同步最终会取得所有状态数据并保存在本地,然后节点就可以切换到完全同步状态。
我们将执行每个块所需的数据集称为 “区块见证数据”(block witness)。得益于默克尔树的结构,我们不用完整地下载某个状态,就可以证明见证数据真的是从这个状态中取出来的(译者注:就是所谓的 “默克尔证明”)。
区块见证数据大小
为了简单起见,我们用 “区块见证数据大小” 来指称执行区块所需的数据元素数量。这类数据元素可能是在主要账户状态树上的一个节点,或者是合约存储树上的一个节点,或者是某个合约的完整字节码(译者注:此处说的 “状态树”、“存储树” 都是默克尔树结构的数据)。
分析区块见证数据大小是理解 Beam 同步方法性能的关键。快速同步方法必须在执行第一个区块(即启动块)之前下载全部状态数据,而 Beam 同步仅需要下载一个区块的见证数据,如果所下载的区块见证数据包含完整状态的三分之一,那么 Beam 同步的运行效率将大约是快速同步的三倍。
所以,显然,下一个步骤是看主网的见证数据实际是多大。可能直接下结论还为时尚早,但是早期试验结果表明 3000 个状态树节点的数据量是一个合理的估计(90% 置信度)。而主网的全部状态信息有超过 3 亿个树节点。
Beam 同步的速度提升
让我们新定义一个标准:“从启动到执行” 的时间。这是从用空数据库启动节点到完成最近一个区块的完全导入所需的时间。
如果 Beam 仅需要下载 3000 个状态树节点,而快速同步需要下载 3 亿个树节点,那么我们就可以确定 Beam Sync 方法的速度上限:在同步主网时,可在 “从启动到执行” 时间上获得最多 10 万倍的提升!
但是,Beam Sync 方法往往无法真正做到 10 万倍提升。理由包括但不限于:
状态数据的下载并不是建立全节点的所有工作,例如,我们还要下载区块头来验证我们所同步的区块链是最长链 区块见证数据是按需确定的,这就意味着我们无法提前预知需要哪些状态数据(收到一个数据,才能确定下一个需要的数据是什么)。所以我们向对等节点请求数据时,一次只能请求一个状态数据。相反,快速同步一次最多可请求 384 个树节点,这使得 Beam 同步对对等节点的网络延迟更敏感。 寻找高质量,低延迟的同步节点需要花些时间。说实在的,会遇上什么样的对等节点,那是真·随机事件。
Beam 同步滞后性
Beam Sync Pivot
Trinity 客户端上的 Beam 同步
原型公布
剩下的工作
Beam Sync 的创新之处在哪里?
(完)
(文内有许多超链接,可点击左下 ”阅读原文“ 从 EthFans 网站上获取)
原文链接:
https://medium.com/@jason.carver/intro-to-beam-sync-a0fd168be14a
作者: Jason Carver
你可能还喜欢: